package genetico;

import java.util.Random;

import principal.EstandarxDLG;
/**
 * NOTA:
 * Para dar los parametros del AG: se accede directamente a los campos publicos.
 * clase DLG
 * LAMBDAS VALIDOS
 * P[]
 */
public abstract class AlgoritmoGeneticoMaxVer implements Runnable{
	private static final int NUMCAMBIO = 10;//cambia de metodo calculo error
	/**
	 * parametros AG
	 */
	public double probabilidadCruce;
	public double probabilidadMutacion;
	public int generaciones;
	/**
	 * numero de cromosomas es la poblacion,!=tamano de la muestra
	 */
	public int poblacion;
	/**
	 * cada cromosoma contiene los 4 lambdas
	 */
	protected  double[][] cromosomas;
	protected  double[] rendimiento;
	/**
	 * da el rango de valores para c/lambda en la poblacion inicial
	 */
	public double valorMin;
	public double valorMax;
	/**
	 * parametros moduloDLG: x=Q(p),Q es la fpercentil
	 */
	public double muestra[];
	volatile public double la1;
	volatile public double la2;
	volatile public double la3;
	volatile public double la4;
	/**
	 * criterio de parada
	 */
	public double errorMax;
	public double errorMaxDePoblacion;
	/**
	 * reajuste periodico de Fx
	 */
	protected boolean calculoConFxMuestral=true;
	/**
	 * aleatorio
	 */
	protected Random rd = new Random(System.currentTimeMillis());
	
	EstandarxDLG panelPrincipal;
	
	/**
	 * Constructor
	 */
	public AlgoritmoGeneticoMaxVer(EstandarxDLG estandarxDLG) {
		this.panelPrincipal=estandarxDLG;
		//poblacion=:cromosomas.length
		this.probabilidadCruce = 0.9d;
		this.probabilidadMutacion = 0.4d;
		this.poblacion = 100;
		this.generaciones = 50;
		this.errorMax=0.01d;
		this.valorMax=1000;
		this.valorMin=-1000;
	}
	public AlgoritmoGeneticoMaxVer() {
		//poblacion=:cromosomas.length
		this.probabilidadCruce = 0.9d;
		this.probabilidadMutacion = 0.4d;
		this.poblacion = 100;
		this.generaciones = 50;
		this.errorMax=0.01d;
		this.valorMax=1000;
		this.valorMin=-1000;
	}
	/**
	 * Ejecuta el AG
	 */
	public void run() {
//		barra de progreso %, y forma de cancelar
		panelPrincipal.progreso.inicio();
		
		errorMaxDePoblacion=Double.MAX_VALUE;
		//AG
		poblacionInicial();
		crearVectorRendimientos();
		calcularRendimientoIndividuos();
		ordenarPorRendimiento();
		int contador=0;
		for (int i = 0; errorMaxDePoblacion > errorMax && i < generaciones && !Thread.interrupted(); i++) {
			////////////////// GUI: MUESTRA EL PROGRESO //////////////
			int porcent=((i + 1) * 100)/(generaciones);
			int seg=(generaciones-i)*poblacion/32;
			panelPrincipal.progreso.tiempoTxt.setText(seg+" segundos");
			panelPrincipal.progreso.progressBar.setValue(porcent);
			//se puede incluir el caso en que se suspenda la iteracion y se guarde en la BD
			String cons="Error="+errorMaxDePoblacion+"\tf(x)="+rendimiento[0];
			//panelPrincipal.progreso.consola.setText(cons);
			System.out.println(porcent +"% ("+seg+") seg	"+cons);
			//////////////////////////////////////////////////////////
			
			///////////////////// EVOLUCION //////////////////////////
			evolucionarPoblacion();
			generarMutaciones();
			calcularRendimientoIndividuos();
			ordenarPorRendimiento();
			if (contador%NUMCAMBIO==0) {
				calculoConFxMuestral=!calculoConFxMuestral;
			}
			contador++;
			///////////////////// EVOLUCION //////////////////////////
		}
		la1=cromosomas[0][0];
		la2=cromosomas[0][1];
		la3=cromosomas[0][2];
		la4=cromosomas[0][3];
		
		System.out.println("\n-solucion:");
		System.out.println(la1);
		System.out.println(la2);
		System.out.println(la3);
		System.out.println(la4);
		
		//Guardar_BD(muestra,L1,L2,L3,L4,error);
//		fin: avisa para mostrar y guardar soluciones
		panelPrincipal.progreso.fin();
	}
	/**
	 * Esta funcion depende del metodo utilizado 
	 */
	protected abstract double calcularRendimientoConFxMuestral(double[] cromo);
	protected abstract double calcularRendimientoConFxLambdas(double[] cromo);
	/**
	 * Esta funcion depende del metodo utilizado, debe llamarse justo despues de 
	 * calcular rendimiento, porque toma los lambdas actuales. 
	 */
	protected abstract double calcularErrorConFxMuestral();
	protected abstract double calcularErrorConFxLambdas();
	
	/**
	 * calcula Rendimiento para c/cromosoma
	 */
	protected  void calcularRendimientoIndividuos() {
		double errori,mejor=-Double.MAX_VALUE;
		for (int i = 0; i < poblacion; i++) {
			if(calculoConFxMuestral){ 
				rendimiento[i] = calcularRendimientoConFxMuestral(cromosomas[i]);
				errori=calcularErrorConFxMuestral();//del cromosomas[i]
			}else{
				rendimiento[i] = calcularRendimientoConFxLambdas(cromosomas[i]);
				errori=calcularErrorConFxLambdas();//del cromosomas[i]
				calculoConFxMuestral=false;
			}
			
			if (rendimiento[i] > mejor) {
				mejor = rendimiento[i];
				errorMaxDePoblacion=errori;
			}
		}
	}
	protected void crearCromosoma(double[] cromo) {
		cromo[0] = (valorMax- valorMin) * rd.nextDouble() + valorMin;
		cromo[1] = (valorMax- valorMin) * rd.nextDouble() + valorMin;
		cromo[2] = (valorMax- valorMin) * rd.nextDouble() + valorMin;
		cromo[3] = (valorMax- valorMin) * rd.nextDouble() + valorMin;
	}
	/**
	 * crea poblacion
	 */
	protected  void poblacionInicial() {
		cromosomas=new double[poblacion][4];
		// Generar cada individuo
		for (int i = 0; i < poblacion; i++) {
			crearCromosoma(cromosomas[i]);
		}
	}
	/**
	 * Genera todas las mutaciones
	 */
	protected  void generarMutaciones() {
		for (int i = 0; i < poblacion; i++) {
			if (rd.nextDouble() <= probabilidadMutacion) {
				double[] individuo = cromosomas[i];
				int j = (int) (4 * rd.nextDouble());
				double ch = individuo[j] * (rd.nextDouble() - 0.5d) / 10;
				individuo[j] += ch;//le suma a lambda +/- 5%
			}
		}
	}
	/**
	 * genera todos los cruces
	 * triple cruce (tres hijos)
	 */
	protected  void evolucionarPoblacion() {
		int cant = (int) (probabilidadCruce * poblacion);//# de indiv a evolucionar
		for (int i = 0,j=poblacion-1; i < cant ; i=i+3,j--) {
			cromosomas[j] = cruzar( cromosomas[i], cromosomas[i + 1] );
			cromosomas[j-1] = cruzar2( cromosomas[i+1], cromosomas[i + 2] );
			cromosomas[j-2] = cruzar3( cromosomas[i+2], cromosomas[i + 3] );
		}
	}

	protected  double[] cruzar2(double[] cromosoma, double[] cromosoma2) {
		double cromosomaCruce[] = new double[4];
		cromosomaCruce[0] = cromosoma[0];
		cromosomaCruce[1] = cromosoma2[1];
		cromosomaCruce[2] = cromosoma[2];
		cromosomaCruce[3] = cromosoma2[3];

		mutarSiEsDuplicado(cromosomaCruce);
		return cromosomaCruce;
	}

	//nuevo cruce
	protected  double[] cruzar3(double[] cromosoma, double[] cromosoma2) {
		double cromosomaCruce[] = new double[4];
		cromosomaCruce[0] = (cromosoma2[0] ) ;
		cromosomaCruce[1] = (cromosoma[1] ) ;
		cromosomaCruce[2] = (cromosoma2[2] ) ;
		cromosomaCruce[3] = (cromosoma[3] ) ;

		mutarSiEsDuplicado(cromosomaCruce);
		return cromosomaCruce;
	}
	/**
	 * @param cromosoma
	 * @param cromosoma2
	 * @return cromosomaCruce
	 */
	protected  double[] cruzar(double[] cromosoma, double[] cromosoma2) {
		double cromosomaCruce[] = new double[4];
		for (int i = 0; i < 4; i++) {
			cromosomaCruce[i] = (cromosoma[i] + cromosoma2[i]) / 2;
		}
		mutarSiEsDuplicado(cromosomaCruce);
		return cromosomaCruce;
	}
	/**
	 * ordena de mayor a menor fitness
	 * ordena el cromosoma y su correspondiente fitness en el array de rendimiento
	 */
	protected  void ordenarPorRendimiento() {
		for (int i = 0; i < poblacion - 1; i++) {
			if (rendimiento[i] < rendimiento[i + 1]) {
				double[] temp = cromosomas[i];
				cromosomas[i] = cromosomas[i + 1];
				cromosomas[i + 1] = temp;
				double  temp2 = rendimiento[i];
				rendimiento[i] = rendimiento[i+1];
				rendimiento[i+1] = temp2;
				if (i > 0) {
					i -= 2;
				}
			}
		}
		//despues de ordenar el mejor siempre estara en la casilla 0
	}
	/**
	 * "borra" o crea el vector
	 */
	protected void crearVectorRendimientos() {
		if(rendimiento==null || cromosomas.length!=rendimiento.length){
			rendimiento=new double[cromosomas.length];
		}
	}

	private void mutarSiEsDuplicado(double[] c) {
		double[] cromo;
		for (int i = 0; i < cromosomas.length; i++) {
			cromo=cromosomas[i];
			if(cromo[0] == c[0] &&
				cromo[1] == c[1] &&
				cromo[2] == c[2] &&
				cromo[3] == c[3]){
				crearCromosoma(cromo);
				return;
			}
		}
	}
	
}
